package me.yangbajing.model

import org.squeryl.KeyedEntity
import org.joda.time.DateTime

import net.liftweb.common.{Full, Failure, Empty, Box}
import net.liftweb.util.StringHelpers

import yangbajing.persistence.SquerylEntrypoint._
import yangbajing.util.Imports.Y
import yangbajing.util.Pagination
import org.squeryl.annotations.Transient

object MUser {
  def modify(id: String, user: ReqUser): Box[MUser] =
    Y.tryBox {
      require(allowedId(id), s"账号不符合要求")

      val ret = transaction(update(Entities.users)(u =>
        where(u.id === id
        ) set(
          u.nick := user.nick,
          u.description := user.description,
          u.signature := user.signature,
          u.contact := user.contact
          )))

      if (ret != 1) throw new RuntimeException("更新数据库失败")
      else findOne(id).openOrThrowException("不能获取账号：" + id)
    }

  def modifyPassword(userId: String, oldPassword: String, newPassword: String): Box[Int] =
    Y.tryBox {
      require(allowedPassword(oldPassword), "原密码不符合要求")
      require(allowedPassword(newPassword), "新密码不符合要求")

      require(findOne(userId, oldPassword).isDefined, "原密码不匹配")

      val (salt, pwd) = makePassword(newPassword)
      transaction(update(Entities.userPasswords)(up =>
        where(up.id === userId
        ) set(up.salt := salt, up.password := pwd)))
    }

  def removeById(id: String): Box[Int] =
    Y.tryBox(transaction {
      Entities.users.deleteWhere(_.id === id)
      Entities.userPasswords.deleteWhere(_.id === id)
    })

  def existsById(id: String): Boolean = size(id = Y.option(id)) > 0L

  def insert(user: MUser, password: String): Box[MUser] =
    Y.tryBox(transaction {
      require(allowedId(user.id), s"账号无效")
      require(Y.emailValidate(user.email), s"邮箱地址无效")

      val ret = Entities.users insert user
      insertPassword(user.id, password)

      ret
    })

  def insertPassword(id: String, password: String) =
    inTransaction {
      require(allowedPassword(password), s"密码无效")
      val (salt, pwd) = makePassword(password)
      Entities.userPasswords insert MUserPassword(id, salt, pwd)
    }

  /**
   *
   * @param password
   * @param len
   * @return (Salt, 加密后密码)
   */
  def makePassword(password: String, len: Int = 10): (String, String) = {
    val salt = StringHelpers.randomString(len)
    (salt, Y.ySha1(salt + password))
  }

  def avatarUri(id: String): String =
    "/assets/img/yangbajing.png"

  def findOne(id: String): Box[MUser] =
    transaction(from(Entities.users)(u => where(u.id === id or u.email === id) select u).singleOption)

  def findOne(id: String, password: String): Box[MUser] =
    Y.tryBox(transaction {
      if (!Y.emailValidate(id))
        require(allowedId(id), s"账号无效")

      require(allowedPassword(password), s"密码无效")

      val (u, up) = from(Entities.users, Entities.userPasswords)((u, up) =>
        where(
          (u.id === id or u.email === id) and
            (u.id === up.id)
        ) select (u -> up)).single

      require(equalPassword(up, password), s"账号：${id} 密码错误。")

      u
    })

  def size(gteCreatedAt: Option[DateTime] = None, lteCreatedAt: Option[DateTime] = None, id: Option[String] = None): Long =
    transaction(from(Entities.users)(u =>
      where(
        u.created_at gte gteCreatedAt.? and
          (u.created_at lte lteCreatedAt.?) and
          (u.id === id.?)
      ) compute count(u.id)))

  def find(offset: Int, limit: Int, gteCreatedAt: Option[DateTime] = None, lteCreatedAt: Option[DateTime] = None) =
    transaction(from(Entities.users)(u =>
      where(
        u.created_at gte gteCreatedAt.? and
          (u.created_at lte lteCreatedAt.?)
      ) select u orderBy u.created_at.desc).page(offset, limit).toList)

  def pagination(_curPage: Int, _limit: Int, gteCreatedAt: Option[DateTime] = None, lteCreatedAt: Option[DateTime] = None) =
    new Pagination[MUser] {
      val curPage = _curPage
      val limit = _limit
      val total = size()
      val page = find(offset, limit, gteCreatedAt = gteCreatedAt, lteCreatedAt = lteCreatedAt)
    }

  def equalPassword(up: MUserPassword, password: String) =
    allowedPassword(password) &&
      (Y.ySha1(up.salt + password) == up.password)

  def allowedId(id: String) =
    (id ne null) &&
      id.length > 1

  def allowedPassword(password: String) =
    (password ne null) &&
      password.length > 5
}

case class MUser(
                  id: String,
                  email: String,
                  contact: Option[String] = None, // 联系方式
                  description: Option[String] = None, // 个人简介
                  nick: Option[String] = None,
                  signature: Option[String] = None,
                  created_at: DateTime = DateTime.now) extends KeyedEntity[String] {
  @Transient
  lazy val nickOrId = nick getOrElse id
}

case class MUserPassword(
                          id: String,
                          salt: String,
                          password: String) extends KeyedEntity[String]
